feat: 콘텐츠 관리 - 콘텐츠 수정, 삭제 기능 구현#157
Conversation
- 기존 썸네일 교체 시 상태 DELETED로 변경
- UserRepository: N+1 문제 방지 update용 조회 메서드 추가
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthrough콘텐츠 수정·삭제용 PATCH/DELETE 엔드포인트와 관련 DTO, 예외, 서비스 로직, OpenAPI 문서, 테스트가 추가되었다. 썸네일 교체·삭제 시 업로드 상태 Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/test/java/com/codeit/team5/mopl/user/service/UserServiceTest.java (1)
199-227: 📐 Maintainability & Code Quality | 🔵 Trivial기존 프로필 이미지 교체 시
DELETED전환 테스트를 추가하세요.현재
update_withImage_success()는User.create(...)로 시작해서profileImage == null상태입니다. 그래서oldProfile != null분기가 실행되지 않아, 기존 이미지를 새 이미지로 바꿀 때 이전BinaryContent가DELETED가 되는 핵심 동작이 빠져 있습니다.
- 권장: 기존
profileImage를 가진 사용자로 교체 후oldProfile.getUploadStatus() == DELETED를 단언하는 별도 테스트 추가
- 장점: 누락된 분기를 직접 커버해 회귀 방지에 가장 유리
- 단점: 테스트가 하나 늘어남
- 대안: 기존
update_withImage_success()에 교체 시나리오를 섞기
- 장점: 변경량이 적음
- 단점: 신규 첨부와 교체 케이스가 섞여 원인 분리가 어려움
ContentServiceTest에 같은 패턴의 테스트가 있으니, 사용자 프로필 이미지도 같은 방식으로 맞추는 편이 자연스럽습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/java/com/codeit/team5/mopl/user/service/UserServiceTest.java` around lines 199 - 227, 현재 update_withImage_success()는 새 프로필 이미지만 붙는 경우만 검증해서, 기존 이미지가 있던 사용자의 교체 시 oldProfile이 DELETED로 전환되는 분기를 커버하지 못합니다. UserService.update와 관련된 프로필 이미지 교체 경로를 직접 검증하는 별도 테스트를 추가하거나 기존 테스트를 교체 시나리오로 바꿔서, User.create 대신 기존 profileImage를 가진 User를 준비한 뒤 oldProfile.getUploadStatus()가 DELETED로 바뀌는지 단언하세요. ContentServiceTest의 동일한 교체 패턴을 참고해 UserServiceTest에서도 BinaryContent와 eventPublisher 흐름을 함께 확인하면 됩니다.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/com/codeit/team5/mopl/content/controller/ContentController.java`:
- Around line 44-62: `patchContent`와 `deleteContent`가 ADMIN 전용이어야 하는데 현재는
`permitAll()` 설정 때문에 누구나 호출할 수 있습니다. `ContentController`의
`patchContent`/`deleteContent`에 메서드 보안을 적용하는
방식(`@PreAuthorize("hasRole('ADMIN')")`) 또는 `SecurityConfig`에서
`/api/contents/**`를 `hasRole("ADMIN")`으로 제한하는 방식으로 막고, 기존 `ContentController`와
`SecurityConfig` 설정이 OpenAPI의 `[어드민]` 표기와 일치하도록 정리해 주세요.
In `@src/main/java/com/codeit/team5/mopl/content/service/ContentService.java`:
- Around line 104-107: The variable name inside storeThumbnailImage currently
uses profileImage, which is misleading for thumbnail handling. Rename the local
variable in ContentService.storeThumbnailImage to thumbnail (and keep any
related uses aligned) so the identifier matches the method’s purpose and avoids
confusion with profile image logic.
- Around line 77-79: The tag update flow in ContentService currently clears all
tags and reattaches them, which can cause duplicate-key conflicts on the
content_tags composite PK and unnecessary DML. Update the tag handling in
ContentService.update by either computing the diff between existing and
requested tags and only removing/adding changes, or by forcing the deletes to be
flushed before attachTags runs if you keep the clear-and-reinsert approach. Use
the existing clearTags and attachTags methods as the main touchpoints.
In
`@src/main/java/com/codeit/team5/mopl/watcher/repository/WatchingSessionRepository.java`:
- Around line 24-25: The `WatchingSessionRepository.findByContentId(...)` query
is using an entity graph that includes the collection path `content.contentTags`
and `content.contentTags.tag`, which can distort `Window`/`ScrollPosition`
paging boundaries. Update the `@EntityGraph` on `findByContentId(...)` to keep
only the to-one associations (`user`, `content`, `content.thumbnail`,
`content.stats`) and remove the collection graph from this method. Load tags
separately, such as via a follow-up query or batch fetching, so the scrolling
window stays stable.
In `@src/test/java/com/codeit/team5/mopl/content/service/ContentServiceTest.java`:
- Around line 339-369: The update_allBlankTags_throwsException test should also
verify that no partial entity mutation occurs before validation fails. Add a
post-exception assertion on the Content created in the test to confirm its title
remains unchanged, so ContentService.update is forced to keep tag validation
before calling content.update and not leave a modified entity state on failure.
- Around line 312-337: `ContentService.update()` and `delete()` currently only
mark the old `BinaryContent` as `DELETED`, but they do not clean up the
underlying storage object, so the fix should separate the DB status change from
actual blob removal by introducing a storage-delete flow in
`BinaryContentStorage` or a delete event/listener. Update the
`update_withNewThumbnail_oldThumbnailMarkedDeleted`-style coverage to assert the
cleanup path as well as the status transition, and make sure
`ContentService.update()`, `ContentService.delete()`, and the thumbnail
replacement logic in `Content`/`BinaryContent` are wired to the new deletion
mechanism.
---
Outside diff comments:
In `@src/test/java/com/codeit/team5/mopl/user/service/UserServiceTest.java`:
- Around line 199-227: 현재 update_withImage_success()는 새 프로필 이미지만 붙는 경우만 검증해서, 기존
이미지가 있던 사용자의 교체 시 oldProfile이 DELETED로 전환되는 분기를 커버하지 못합니다. UserService.update와
관련된 프로필 이미지 교체 경로를 직접 검증하는 별도 테스트를 추가하거나 기존 테스트를 교체 시나리오로 바꿔서, User.create 대신 기존
profileImage를 가진 User를 준비한 뒤 oldProfile.getUploadStatus()가 DELETED로 바뀌는지 단언하세요.
ContentServiceTest의 동일한 교체 패턴을 참고해 UserServiceTest에서도 BinaryContent와
eventPublisher 흐름을 함께 확인하면 됩니다.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: cc20e51e-fa1b-4bda-9e99-61b4302f8290
📒 Files selected for processing (20)
src/main/java/com/codeit/team5/mopl/binarycontent/entity/BinaryContentUploadStatus.javasrc/main/java/com/codeit/team5/mopl/content/controller/ContentController.javasrc/main/java/com/codeit/team5/mopl/content/controller/api/ContentApi.javasrc/main/java/com/codeit/team5/mopl/content/dto/request/ContentCreateRequest.javasrc/main/java/com/codeit/team5/mopl/content/dto/request/ContentUpdateRequest.javasrc/main/java/com/codeit/team5/mopl/content/entity/Content.javasrc/main/java/com/codeit/team5/mopl/content/exception/ContentException.javasrc/main/java/com/codeit/team5/mopl/content/exception/ContentNotFoundException.javasrc/main/java/com/codeit/team5/mopl/content/exception/InvalidContentTitleException.javasrc/main/java/com/codeit/team5/mopl/content/exception/TooManyTagsException.javasrc/main/java/com/codeit/team5/mopl/content/repository/ContentRepository.javasrc/main/java/com/codeit/team5/mopl/content/service/ContentService.javasrc/main/java/com/codeit/team5/mopl/user/repository/UserRepository.javasrc/main/java/com/codeit/team5/mopl/user/service/UserService.javasrc/main/java/com/codeit/team5/mopl/watcher/repository/WatchingSessionRepository.javasrc/main/resources/db/migration/V11__add_deleted_status_to_binary_contents.sqlsrc/test/java/com/codeit/team5/mopl/content/controller/ContentControllerTest.javasrc/test/java/com/codeit/team5/mopl/content/repository/ContentRepositoryTest.javasrc/test/java/com/codeit/team5/mopl/content/service/ContentServiceTest.javasrc/test/java/com/codeit/team5/mopl/user/service/UserServiceTest.java
|
수고하셨습니다! 전체적으로 잘 작성하신 것 같습니다 |
관련 이슈
작업 내용
변경 사항
테스트 내용
체크리스트
리뷰 포인트
스크린샷 / 참고 자료
🔗 관련 이슈
#11#18#101#103📝 작업 내용
기존에는 콘텐츠 생성 기능은 어느 정도 흐름이 있었지만, 운영에서 필요한 콘텐츠 수정/삭제가 API-도메인-서비스-예외-테스트 전반에서 일관되게 연결되지 못했습니다. 특히
가 정리되어 있지 않았습니다.
이번 PR은 콘텐츠 수정/삭제 요청이 실제 도메인 갱신 및 리소스 정리 흐름으로 이어지도록, DTO/예외/오픈API 및 테스트까지 함께 보강했습니다. 또한 기존 썸네일 리소스의 정리 흐름을 명시하기 위해
binary_contents업로드 상태 체계를 확장했습니다.주요 변경사항
콘텐츠 수정/삭제 API 계약 확장 및 문서 정리
ContentController에PATCH /contents/{contentId}(제목/설명/태그 + 선택적thumbnail)와DELETE /contents/{contentId}를 추가했습니다.ContentApiOpenAPI 스키마(요청 multipart 스키마/응답 오류 스키마)를 수정/삭제 계약에 맞춰 정리했습니다.기존 썸네일 교체/삭제 시 “정리 상태”를 업로드 상태로 모델링
BinaryContentUploadStatus에DELETED상태를 추가하고, DB 마이그레이션으로binary_contents의 CHECK 제약에DELETED를 허용하도록 갱신했습니다.ContentService.update(...)/delete(...)에서 기존 썸네일이 존재하면 해당BinaryContent의 업로드 상태를DELETED로 전환한 뒤 후속 처리를 진행하도록 변경했습니다.storeThumbnailImage(...)로 썸네일 저장 및 업로드 이벤트 발행 흐름을 정리하고, 업데이트 시에는 새 썸네일이 들어온 경우에만 첨부를 수행하도록 분기했습니다.태그 사용량 제한 및 예외 의미 강화
TooManyTagsException(400)로 명확히 실패하도록 처리했습니다.InvalidContentTitleException으로 구체화했고, 예외 처리에서 details(상세 정보) 포함 가능하도록ContentException생성자 형태를 확장했습니다.ContentNotFoundException은contentId를 포함해 반환하도록 변경했습니다.도메인/서비스 흐름 및 조회 성능 개선
StringUtils.hasText기반 제목 검증을 도메인 레벨로 반영하는 식으로 흐름을 정리했습니다.findById()에서findWithProfileImageById()로 변경했습니다.@EntityGraph범위를 확장해content.thumbnail,content.contentTags,content.contentTags.tag까지 함께 로딩되도록 조정했습니다.테스트 보강(Controller/Service/Repository)
ContentControllerTest: PATCH/DELETE의 정상 케이스 및 400/404/500 예외 응답(유효성/존재 여부/서비스 오류)을 함께 검증하도록 확장했습니다.ContentServiceTest: 썸네일 유무/교체/삭제 시DELETED상태 전환 및 의존성 호출 여부, 태그 검증 실패 및 존재하지 않는 콘텐츠 처리까지 시나리오별로 검증했습니다.ContentRepositoryTest:findWithStatsAndTagsById가 필요한 연관 데이터를 fetch join으로 가져오고 쿼리 수를 의도대로 유지하는지 검증했습니다.UserServiceTest: 프로필 업데이트 테스트 조회 경로를findWithProfileImageById로 맞추도록 조정했습니다.📊 변경 효과 요약 (가능한 경우에만)
ContentRepositoryTest신규 추가 1건,ContentControllerTest/ContentServiceTest확장,UserServiceTest수정까지 포함해 수정/삭제 기능의 핵심 시나리오를 넓혔습니다.